package com.skivingcloud.report.congfig;

import com.zaxxer.hikari.HikariDataSource;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;

@Component
@ConfigurationProperties(prefix = "spring.datasource")
public class DatasourceProperties {
    /**
     * 初始化时建立物理连接的个数。初始化发生在显示调用init方法，或者第一次getConnection时
     */
    private int initialSize = 5;

    /**
     * 最小连接池数量
     */
    private int minIdle = 5;

    /**
     * 最大连接池数量
     */
    private int maxActive = 30;

    /**
     * 获取连接时最大等待时间，单位毫秒。配置了maxWait之后，缺省启用公平锁，并发效率会有所下降，如果需要可以通过配置
     */
    private int maxWait = 60000;

    /**
     * 关闭空闲连接的检测时间间隔.Destroy线程会检测连接的间隔时间，如果连接空闲时间大于等于minEvictableIdleTimeMillis则关闭物理连接。
     */
    private int timeBetweenEvictionRunsMillis = 60000;

    /**
     * 连接的最小生存时间.连接保持空闲而不被驱逐的最小时间
     */
    private int minEvictableIdleTimeMillis = 300000;

    /**
     * 申请连接时检测空闲时间，根据空闲时间再检测连接是否有效.建议配置为true，不影响性能，并且保证安全性。申请连接的时候检测，如果空闲时间大于timeBetweenEvictionRun
     */
    private boolean testWhileIdle = true;

    /**
     * 开启PSCache
     */
    private boolean poolPreparedStatements = false;

    /**
     * 设置PSCache值
     */
    private int maxPoolPreparedStatementPerConnectionSize;

    /**
     * 连接出错后再尝试连接三次
     */
    private int connectionErrorRetryAttempts = 3;

    /**
     * 数据库服务宕机自动重连机制
     */
    private boolean breakAfterAcquireFailure = true;

    /**
     * 连接出错后重试时间间隔
     */
    private int timeBetweenConnectErrorMillis;

    /**
	 * @return the initialSize
	 */
	public int getInitialSize() {
		return initialSize;
	}

	/**
	 * @param initialSize the initialSize to set
	 */
	public void setInitialSize(int initialSize) {
		this.initialSize = initialSize;
	}

	/**
	 * @return the minIdle
	 */
	public int getMinIdle() {
		return minIdle;
	}

	/**
	 * @param minIdle the minIdle to set
	 */
	public void setMinIdle(int minIdle) {
		this.minIdle = minIdle;
	}

	/**
	 * @return the maxActive
	 */
	public int getMaxActive() {
		return maxActive;
	}

	/**
	 * @param maxActive the maxActive to set
	 */
	public void setMaxActive(int maxActive) {
		this.maxActive = maxActive;
	}

	/**
	 * @return the maxWait
	 */
	public int getMaxWait() {
		return maxWait;
	}

	/**
	 * @param maxWait the maxWait to set
	 */
	public void setMaxWait(int maxWait) {
		this.maxWait = maxWait;
	}

	/**
	 * @return the timeBetweenEvictionRunsMillis
	 */
	public int getTimeBetweenEvictionRunsMillis() {
		return timeBetweenEvictionRunsMillis;
	}

	/**
	 * @param timeBetweenEvictionRunsMillis the timeBetweenEvictionRunsMillis to set
	 */
	public void setTimeBetweenEvictionRunsMillis(int timeBetweenEvictionRunsMillis) {
		this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
	}

	/**
	 * @return the minEvictableIdleTimeMillis
	 */
	public int getMinEvictableIdleTimeMillis() {
		return minEvictableIdleTimeMillis;
	}

	/**
	 * @param minEvictableIdleTimeMillis the minEvictableIdleTimeMillis to set
	 */
	public void setMinEvictableIdleTimeMillis(int minEvictableIdleTimeMillis) {
		this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
	}

	/**
	 * @return the testWhileIdle
	 */
	public boolean isTestWhileIdle() {
		return testWhileIdle;
	}

	/**
	 * @param testWhileIdle the testWhileIdle to set
	 */
	public void setTestWhileIdle(boolean testWhileIdle) {
		this.testWhileIdle = testWhileIdle;
	}

	/**
	 * @return the poolPreparedStatements
	 */
	public boolean isPoolPreparedStatements() {
		return poolPreparedStatements;
	}

	/**
	 * @param poolPreparedStatements the poolPreparedStatements to set
	 */
	public void setPoolPreparedStatements(boolean poolPreparedStatements) {
		this.poolPreparedStatements = poolPreparedStatements;
	}

	/**
	 * @return the maxPoolPreparedStatementPerConnectionSize
	 */
	public int getMaxPoolPreparedStatementPerConnectionSize() {
		return maxPoolPreparedStatementPerConnectionSize;
	}

	/**
	 * @param maxPoolPreparedStatementPerConnectionSize the maxPoolPreparedStatementPerConnectionSize to set
	 */
	public void setMaxPoolPreparedStatementPerConnectionSize(int maxPoolPreparedStatementPerConnectionSize) {
		this.maxPoolPreparedStatementPerConnectionSize = maxPoolPreparedStatementPerConnectionSize;
	}

	/**
	 * @return the connectionErrorRetryAttempts
	 */
	public int getConnectionErrorRetryAttempts() {
		return connectionErrorRetryAttempts;
	}

	/**
	 * @param connectionErrorRetryAttempts the connectionErrorRetryAttempts to set
	 */
	public void setConnectionErrorRetryAttempts(int connectionErrorRetryAttempts) {
		this.connectionErrorRetryAttempts = connectionErrorRetryAttempts;
	}

	/**
	 * @return the breakAfterAcquireFailure
	 */
	public boolean isBreakAfterAcquireFailure() {
		return breakAfterAcquireFailure;
	}

	/**
	 * @param breakAfterAcquireFailure the breakAfterAcquireFailure to set
	 */
	public void setBreakAfterAcquireFailure(boolean breakAfterAcquireFailure) {
		this.breakAfterAcquireFailure = breakAfterAcquireFailure;
	}

	/**
	 * @return the timeBetweenConnectErrorMillis
	 */
	public int getTimeBetweenConnectErrorMillis() {
		return timeBetweenConnectErrorMillis;
	}

	/**
	 * @param timeBetweenConnectErrorMillis the timeBetweenConnectErrorMillis to set
	 */
	public void setTimeBetweenConnectErrorMillis(int timeBetweenConnectErrorMillis) {
		this.timeBetweenConnectErrorMillis = timeBetweenConnectErrorMillis;
	}

	public DataSource dataSource(String url, String username, String password, String driverClassName) {
		DataSourceProperties properties = new DataSourceProperties();
		properties.setUrl(url);
		properties.setUsername(username);
		properties.setPassword(password);
		properties.setDriverClassName(driverClassName);
		return DatasourceProperties.<HikariDataSource>createHikariDataSource(properties, HikariDataSource.class);
    }
	
	@SuppressWarnings("unchecked")
	private static <T> T createHikariDataSource(DataSourceProperties properties, Class<? extends DataSource> type) {
		return (T) properties.initializeDataSourceBuilder().type(type).build();
	}
}